using Mapster;
using Microsoft.AspNetCore.Http;
using SqlSugar;
using System.Data;
using QiAdmin.Core;
using QiAdmin.Core.Models;
using QiAdmin.Core.Services.Cache;
using QiAdmin.Core.Services.Log;
using QiAdmin.Core.Services.Token;
using QiAdmin.Core.Utils;
using QiAdmin.Core.Utils.Encryption;
using QiAdmin.Core.Utils.Request;
using QiAdmin.Entity;
using QiAdmin.Model.Dtos;
using QiAdmin.Model.Dtos.Input;
using QiAdmin.Model.Dtos.Output;
using QiAdmin.Model.System.Models;
using QiAdmin.Repository.Repository;
using QiAdmin.Service.IService;
using static QiAdmin.Service.Service.TSysUserService;
using ICacheService = QiAdmin.Core.Services.Cache.ICacheService;

namespace QiAdmin.Service.Service
{
    /// <summary>
    /// TSysUser Services
    /// </summary>
    public class TSysUserService : BaseService<TSysUser, TSysUser_IN, TSysUser_OUT>, ITSysUserService
    {
        readonly TSysUserRepository _tSysUserRepository;
        readonly TSysDeptRepository _tSysDeptRepository;
        readonly TSysRoleMenuRepository _tSysRoleMenuRepository;
        readonly TSysLoginLogRepository _tSysLoginLogRepository;

        public TSysUserService(ILogService logService,
            ITokenService tokenService, ICacheService cacheService, IHttpContextAccessor httpContextAccessor, TSysUserRepository tSysUserRepository, TSysDeptRepository tSysDeptRepository, TSysRoleMenuRepository tSysRoleMenuRepository, TSysLoginLogRepository tSysLoginLogRepository) : base(logService, tokenService, cacheService, httpContextAccessor.HttpContext)
        {
            _tSysUserRepository = tSysUserRepository;
            _tSysDeptRepository = tSysDeptRepository;
            _tSysRoleMenuRepository = tSysRoleMenuRepository;
            _tSysLoginLogRepository = tSysLoginLogRepository;
        }

        #region 增删改查-自动生成

        /// <summary>
        /// 创建默认条件
        /// </summary>
        /// <returns></returns>
        public Expressionable<TSysUser> CreateExp()
        {
            var exp = Expressionable.Create<TSysUser>();
            exp.AndIF(Token.UserRole != App.StaticData.SystemRole, t => t.RoleId != App.StaticData.SystemRole);
            var (dataRang, permissions) = GetUserPermission();
            switch (dataRang)
            {
                case DataRang.All:
                    break;
                case DataRang.Dept:
                    exp.And(t => t.DeptId == Token.UserDept);
                    break;
                case DataRang.DeptAndBelow:
                    exp.And(t => GetRecursiveDept().Contains(t.DeptId));
                    break;
                case DataRang.Self:
                    exp.And(t => t.SysCreateUser == Token.UserId);
                    break;
                case DataRang.Custom:
                    exp.And(t => permissions.Contains(t.DeptId));
                    break;
            }
            return exp;
        }

        /// <summary>
        /// 新增单条
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public CommonResult Add(TSysUser_IN input)
        {
            if (_tSysUserRepository.Count(t => t.Account == input.Account) > 0)
            {
                return CommonResult.Convert(false, "该账号已存在");
            }
            string newPwd = StringUtil.GetRandomStr(6, issymbol: false);
            var model = input.Adapt<TSysUser>();
            model.PassWord = Md5Util.Encrypt(newPwd);
            int Id = _tSysUserRepository.InsertReturnIdentity(model);
            return CommonResult.Convert(Id > 0, newPwd);
        }

        /// <summary>
        /// 新增多条
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public CommonResult AddRange(List<TSysUser_IN> input)
        {
            var list = input.Adapt<List<TSysUser>>();
            var result = _tSysUserRepository.InsertRange(list);
            return CommonResult.Convert(result);
        }

        /// <summary>
        /// 编辑
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public CommonResult Edit(TSysUser_IN input)
        {
            if (_tSysUserRepository.Count(t => t.Account == input.Account && t.Id != input.Id) > 0)
            {
                return CommonResult.Convert(false, "该账号已存在");
            }
            var exp = CreateExp();
            exp.And(t => t.Id == input.Id);
            var model = input.Adapt<TSysUser>();
            var result = _tSysUserRepository.UpdateAndIgnore(model, exp.ToExpression());
            AddCheckToken(input.Id);
            return CommonResult.Convert(result);
        }

        /// <summary>
        /// 获取单条
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public CommonResult<TSysUser_OUT> Get(TSysUser_IN input)
        {
            var exp = CreateExp();
            exp.And(t => t.Id == input.Id);

            var result = _tSysUserRepository.GetFirst(exp.ToExpression()).Adapt<TSysUser_OUT>();
            return new CommonResult<TSysUser_OUT>(result);
        }

        /// <summary>
        /// 分页获取
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public CommonResult<PageOutput<TSysUser_OUT>> GetPageList(TSysUser_IN input)
        {
            var exp = CreateExp();

            exp.AndIF(input.StartQueryTime != DateTime.MinValue, t => t.SysCreateTime >= input.StartQueryTime.Date);
            exp.AndIF(input.EndQueryTime != DateTime.MinValue, t => t.SysCreateTime < input.EndQueryTime.Date.AddDays(1));
            exp.AndIF(!string.IsNullOrEmpty(input.Name), t => t.Name.Contains(input.Name));
            PageOutput<TSysUser_OUT> pageOutput = new PageOutput<TSysUser_OUT>();
            PageModel pageModel = new() { PageIndex = input.PageIndex, PageSize = input.PageSize };
            pageOutput.Data = _tSysUserRepository.GetPageList(exp.ToExpression(), pageModel, t => t.Id, OrderByType.Desc).Adapt<List<TSysUser_OUT>>();
            pageOutput.DataCount = pageModel.TotalCount;

            return new CommonResult<PageOutput<TSysUser_OUT>>(pageOutput);
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public CommonResult Remove(TSysUser_IN input)
        {
            var exp = CreateExp();
            exp.And(t => t.Id == input.Id);

            var result = _tSysUserRepository.Delete(exp.ToExpression());
            AddCheckToken(input.Id);
            return CommonResult.Convert(result);
        }

        /// <summary>
        /// 删除多条
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public CommonResult RemoveRange(List<TSysUser_IN> input)
        {
            var exp = CreateExp();
            exp.And(t => input.Select(s => s.Id).Contains(t.Id));

            var result = _tSysUserRepository.Delete(exp.ToExpression());
            for (int i = 0; i < input.Count; i++)
            {
                AddCheckToken(input[i].Id);
            }
            return CommonResult.Convert(result);
        }

        #endregion

        #region Login
        public CommonResult<dynamic> Login(TSysUser_IN input)
        {
            if (input.CaptchaKey.Length != 32)
            {
                return CommonResult<dynamic>.Error;
            }
            string captcha = _CacheService.Get<string>(input.CaptchaKey);
            if (captcha == null)
            {
                return CommonResult<dynamic>.Convert(false, "验证码已过期");
            }
            if (captcha.ToLower() != input.Captcha.ToLower())
            {
                _CacheService.Remove(input.CaptchaKey);
                return CommonResult<dynamic>.Convert(false, "验证码错误");
            }
            _CacheService.Remove(input.CaptchaKey);
            string errorKey = "Login_" + input.Account;
            var errorData = CheckErrorCount(errorKey, 5);
            if (!errorData.IsSuccess)
            {
                return CommonResult<dynamic>.Convert(false, $"该账户已多次密码输入错误，请{errorData.errorData.ErrorTime}后重试");
            }
            var user = _tSysUserRepository.GetFirst(t => t.Account.ToLower() == input.Account.ToLower(), "账户不存在");

            if (user.PassWord != input.PassWord)
            {
                AddErrorCount(errorKey, errorData.errorData, new TimeSpan(2, 0, 0));
                return CommonResult<dynamic>.Convert(false, "密码输入错误");
            }
            if (user.Status != "10")
            {
                return CommonResult<dynamic>.Convert(false, "该账户已被冻结");
            }
            TokenData tokenData = new TokenData
            {
                UserId = user.Id,
                UserAccount = user.Account,
                UserDept = user.DeptId,
                UserRole = user.RoleId,
            };

            _TokenService.RemoveCheckToken(tokenData.UserId);
            string UserToken = _TokenService.GenerateToken(_HttpContext, tokenData);

            #region 添加登录日志
            try
            {
                TSysLoginLog sysLoginLog = new TSysLoginLog();
                sysLoginLog.UserId = user.Id;
                sysLoginLog.DeptId = user.DeptId;
                sysLoginLog.IP = RequestInfoUtil.GetIp(_HttpContext);
                sysLoginLog.IPInfo = RequestInfoUtil.GetIpInfo(sysLoginLog.IP).ToString();
                sysLoginLog.UAStr = RequestInfoUtil.GetUserAgent(_HttpContext);
                var UAInfo = RequestInfoUtil.GetUserAgentInfo(sysLoginLog.UAStr);
                sysLoginLog.Browser = UAInfo.Browser;
                sysLoginLog.OS = UAInfo.OS;
                sysLoginLog.Device = UAInfo.Device;

                _tSysLoginLogRepository.Insert(sysLoginLog);
            }
            catch { }

            #endregion

            return new CommonResult<dynamic>(new { tokenData.UserId, UserName = user.Name, UserToken });
        }
        public CommonResult<dynamic> GetCaptcha()
        {
            string CaptchaKey = Guid.NewGuid().ToString("N");
            string code = StringUtil.GetRandomStr(4, issymbol: false);
            _CacheService.Add(CaptchaKey, code, new TimeSpan(0, 1, 0));
            byte[] bytes = CaptchaUtil.CreateCaptcha(code);
            string base64 = Convert.ToBase64String(bytes);
            return new CommonResult<dynamic>(new { CaptchaKey, Captcha = base64 });
        }

        #endregion

        /// <summary>
        /// 获取用户菜单(树形结构)-导航菜单
        /// </summary>
        /// <returns></returns>
        public CommonResult<List<UserTreeMenu>> GetUserTreeMenus()
        {
            var userMenus = _tSysRoleMenuRepository.Context.Queryable<TSysRoleMenu>().LeftJoin<TSysMenu>((rm, m) => rm.MenuId == m.Id).Where((rm, m) => m.Type == "Menu" && m.IsHide == false && rm.RoleId == Token.UserRole).OrderBy((rm, m) => m.Sort).Select((rm, m) =>
            new UserMenu
            {
                UserId = Token.UserId,
                DeptId = Token.UserDept,
                RoleId = Token.UserRole,
                MenuId = m.Id,
                MenuParentId = m.ParentId,
                MenuName = m.Name,
                MenuComponent = m.Component,
                MenuIcon = m.Icon,
                MenuIsAffix = m.IsAffix,
                MenuIsHide = m.IsHide,
                MenuIsIframe = m.IsIframe,
                MenuIsKeepAlive = m.IsKeepAlive,
                MenuIsLink = m.IsLink,
                MenuPath = m.Path,
                MenuRedirect = m.Redirect,
                MenuTitle = m.Title,
                MenuLinkUrl = m.Url,

            }).ToList();
            var result = ConverTreeMenu(userMenus, 0);
            return new CommonResult<List<UserTreeMenu>>(result);
        }

        private List<UserTreeMenu> ConverTreeMenu(List<UserMenu> userMenus, int parentId)
        {
            List<UserTreeMenu> userTreeMenus = new List<UserTreeMenu>();
            var findAll = userMenus?.FindAll(s => s.MenuParentId == parentId);
            if (findAll.Count > 0)
            {
                foreach (var item in findAll)
                {
                    UserTreeMenu userTreeMenu = new UserTreeMenu();
                    userTreeMenu.path = item.MenuPath;
                    userTreeMenu.name = item.MenuName;
                    userTreeMenu.component = item.MenuComponent;
                    userTreeMenu.redirect = item.MenuRedirect;
                    userTreeMenu.meta = new MenuMeta
                    {
                        title = item.MenuTitle,
                        icon = item.MenuIcon,
                        isAffix = item.MenuIsAffix,
                        isHide = item.MenuIsHide,
                        isIframe = item.MenuIsIframe,
                        isKeepAlive = item.MenuIsKeepAlive,
                        isLink = item.MenuIsLink,
                        url = item.MenuLinkUrl
                    };
                    userTreeMenu.children = ConverTreeMenu(userMenus, item.MenuId);
                    userTreeMenus.Add(userTreeMenu);
                }

            }
            return userTreeMenus;
        }

        public CommonResult<List<UserButton>> GetUserBtns()
        {
            var btns = _tSysRoleMenuRepository.Context.Queryable<TSysRoleMenu>().LeftJoin<TSysMenu>((rm, m) => rm.MenuId == m.Id).Where((rm, m) => rm.RoleId == Token.UserRole && m.Type == "Button").Select((rm, m) => new UserButton { Name = m.Name, Path = m.Path }).ToList();
            return new CommonResult<List<UserButton>>(btns);
        }


        /// <summary>
        /// 重置用户密码
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public CommonResult<string> RestUserPwd(TSysUser_IN input)
        {
            string newPwd = StringUtil.GetRandomStr(6, issymbol: false);
            _tSysUserRepository.UpdateSetColumnsTrue(t => new TSysUser
            {
                PassWord = Md5Util.Encrypt(newPwd)
            }, t => t.Id == input.Id, "密码重置失败");
            AddCheckToken(input.Id);
            return new CommonResult<string>(newPwd);
        }

        /// <summary>
        /// 修改密码
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public CommonResult UpdatePwd(TSysUser_IN input)
        {
            if (input.NewPassword.Length < 6)
            {
                return CommonResult.Convert(false, "密码长度不能小于6为");
            }
            _tSysUserRepository.UpdateSetColumnsTrue(t => new TSysUser
            {
                PassWord = Md5Util.Encrypt(input.NewPassword)
            }, t => t.Id == Token.UserId && t.PassWord == Md5Util.Encrypt(input.PassWord), "原始密码输入错误");
            _TokenService.RemoveToken(_HttpContext);
            return CommonResult.Success;
        }

        /// <summary>
        /// 修改用户信息需要让用户重新登录
        /// </summary>
        /// <param name="userId"></param>
        private void AddCheckToken(int userId)
        {
            _TokenService.AddCheckToken(userId);
        }

        public CommonResult<LoginUserInfo> GetLoginUserInfo()
        {
            var user = _tSysUserRepository.GetById(Token.UserId);
            var cacheRole = _CacheService.Get<List<TSysRole_OUT>>(App.StaticData.CacheKey_Role);
            var loginLog = _tSysLoginLogRepository.GetLast(t => t.UserId == Token.UserId && t.SysCreateTime > DateTime.Now.AddDays(-2).Date);
            LoginUserInfo userInfo = new LoginUserInfo
            {
                Name = user.Name,
                RoleName = cacheRole?.Find(x => x.Id == user.RoleId)?.Name,
                LoginIp = loginLog.IP,
                LoginTime = loginLog.SysCreateTime.ToString(),
                LoginAddr = loginLog.IPInfo,
                LoginBrowser = loginLog.Browser
            };
            return new CommonResult<LoginUserInfo>(userInfo);
        }

        public CommonResult<List<LoginLog>> GetLoginLogs()
        {
            List<LoginLog> loginLogs = new List<LoginLog>();
            var logs = _tSysLoginLogRepository.AsQueryable().GroupBy(t => t.SysCreateTime.ToString("yyyy-MM-dd")).Where(t => t.UserId == Token.UserId && t.SysCreateTime > DateTime.Now.AddDays(-10).Date).Select(t => new LoginLog
            {
                Time = t.SysCreateTime.ToString("yyyy-MM-dd"),
                Count = SqlFunc.AggregateCount(t.Id),
            }).ToList();

            for (int i = 10; i > 0; i--)
            {
                LoginLog loginLog = new LoginLog();
                loginLog.Time = DateTime.Now.AddDays(-i).ToString("yyyy-MM-dd");
                var find = logs.Find(s => s.Time == loginLog.Time);
                if (find != null)
                {
                    loginLog.Count = find.Count;
                }
                loginLogs.Add(loginLog);
            }

            return new CommonResult<List<LoginLog>>(loginLogs);
        }
    }

}